#include "ctf.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

#if   defined(_MSC_VER) && (_MSC_VER>1000)
#pragma warning(disable:996 267)
#endif

#ifndef NULL
#define NULL 0
#endif

ctf_test     _ctf_test = NULL;
ctf_testsuit     _crt_ts = NULL;
ctf_testcase     _crt_tc = NULL;

int _asserted = 0; // assert invoked in current test case

void ctf_begin(char* desc, char* logname)
{
    if (logname == NULL)
        log_init("TestLog.html", RAYLOG_TYPE_HTML + RAYLOG_TYPE_STD);
    else
        log_init(logname, RAYLOG_TYPE_HTML + RAYLOG_TYPE_STD);

    LOG_TITLE("Unit Test Result");
    LOG_HEADER("Unit Test Result");

    assert(_ctf_test == NULL);
    _ctf_test = malloc(sizeof(*_ctf_test));
    _ctf_test->tsFirst = NULL;
    _ctf_test->tsLast = NULL;
    assert(_ctf_test);

    strcpy(_ctf_test->name, desc);
}

void destroy_ts(ctf_testsuit ts)
{
    ctf_testcase tc = ts->tcFirst;
    ctf_testcase tcOld = NULL;

    if (!ts)
        return;

    while(tc)
    {
        tcOld = tc;
        tc = tc->next;
        // TODO: free here cause memory stack error!
        //free(tcOld);
    }
    // and this either
    //free(ts);
}

void ctf_end()
{
    if (_ctf_test != NULL)
    {
        ctf_testsuit ptr = _ctf_test->tsFirst;
        while(ptr)
        {
            ctf_testsuit temp = ptr->next;
            destroy_ts(ptr);
            ptr = temp;
        }
        //free(_ctf_test);
    }
    log_destory();
}

ctf_testsuit ctf_ts_init(char* name, fix_func setup, fix_func teardown)
{
    ctf_testsuit ts;

    if (!_ctf_test)
    {
        printf("ctf_test object should be created first!\n"
            "exiting...\n");
        exit(1);
    }

    ts = malloc(sizeof(struct ctf_testsuit_));
    assert(ts);

    strcpy(ts->name, name);
    ts->setup = setup;
    ts->teardown = teardown;
    ts->failed = 0;
    ts->ran = 0;
    ts->tcFirst = ts->tcLast = NULL;
    ts->next = ts->prev = NULL;

    if (_ctf_test->tsLast)
    {
        _ctf_test->tsLast->next = ts;
        ts->prev = _ctf_test->tsLast;
        _ctf_test->tsLast = ts;
    }
    else
    {
        _ctf_test->tsFirst = _ctf_test->tsLast = ts;
    }
    return ts;
};

void ctf_tc_add(char* name, ctf_testsuit ts, tc_func func)
{
    ctf_testcase ptr, newtc = NULL;
    if (!ts)
    {
        printf("invalid testsuit");
    }

    newtc = malloc(sizeof(*newtc));
    assert(newtc);
    strcpy(newtc->name, name);
    newtc->func = func;
    newtc->failed = RESULT_OK;
    newtc->next = newtc->prev = NULL;

    ptr = ts->tcLast;
    if (ptr)
    {
        ptr->next = newtc;
        newtc->prev = ptr;
        ts->tcLast = newtc;
    }
    else
    {
        ts->tcFirst = ts->tcLast = newtc;
    }
};

void ctf_run()
{
    int count = 0;
    ctf_testsuit ts;
    ctf_testcase tc;
    if (!_ctf_test || !_ctf_test->tsFirst)
    {
        printf("Test should be initialized before running!\n");
        exit(1);
    }
    ts = _ctf_test->tsFirst;

    while(ts)
    {
        char buf[80];
        _crt_ts = ts;

        ts->setup();
        sprintf(buf," Test Suit: %s ", ts->name);
        log_write(ts->name, RAYLOG_SUBHEADER, 3);

        tc = ts->tcFirst;
        while(tc)
        {
            char tcbuf[256];
            _crt_tc = tc;
            _asserted = 0;

            sprintf(tcbuf, "[#%d] %s\n", count++, tc->name);
            LOG_CONTENT(tcbuf, 3);

            if (tc->func(tc) != 0)
            {
                ts->failed++;
            }
            else

            ts->ran++;
            tc = tc->next;
        }

        sprintf(buf, "Test Suit \"%s\" done,  Ran:%d  Failed:%d \n",
            ts->name, ts->ran, ts->failed);

        if (ts->failed)
            log_cwrite(buf, RAYLOG_CONTENT, 0, RAYLOG_RED);
        else
            log_cwrite(buf, RAYLOG_CONTENT, 0, RAYLOG_GREEN);

        ts->teardown();
        ts = ts->next;
    }
};

bool _assert_(char* msg, int cond, int line, char* filename)
{
    char buf[MAX_STR];

    if (cond) return TRUE;

    sprintf(buf, "! Assert failed [%s, %s]\n%s : line %d  with message \"%s\"\n",
        _crt_ts->name,
        _crt_tc->name,
        filename,
        line,
        msg);

    log_cwrite(buf, RAYLOG_LIST, 5, RAYLOG_RED);
    _asserted ++;
    return FALSE;
};
